import type { NormalizedLandmark } from '@mediapipe/tasks-vision'
import {
  Box3,
  BoxGeometry,
  MathUtils,
  Matrix4,
  Mesh,
  MeshBasicMaterial,
  Quaternion,
  Vector3,
  type Group,
  type Object3DEventMap,
  type Scene,
} from 'three'
import { GLTFLoader } from 'three/addons'
import { scaleLandmark } from '@/try-on/landmarks_helps'
import type { Contrast } from './contrast'
function loadModel(file: string) {
  return new Promise<Group<Object3DEventMap>>((resolve, reject) => {
    const loader = new GLTFLoader()
    loader.load(
      file,
      function (gltf) {
        gltf.scene.name = 'glasses'
        resolve(gltf.scene)
      },
      undefined,
      function (err) {
        reject(err)
      },
    )
  })
}
export class Glasses {
  scene: Scene
  width: number
  height: number
  needUpdate: boolean = false
  glasses?: Group<Object3DEventMap>
  scaleFactor?: number
  landmark?: NormalizedLandmark[]
  cube?: Mesh
  contrast: Contrast
  zoom = 1.09
  loading = true
  constructor(scene: Scene, width: number, height: number, contrast: Contrast) {
    this.scene = scene
    this.width = width
    this.height = height
    this.contrast = contrast
    // this.loadGlasses()
    this.createCube()
    this.addCube()
  }
  async loadGlasses(url: string) {
    this.loading = true
    const inScene = this.scene.getObjectByName('glasses')
    if (this.glasses) this.scene.remove(this.glasses)
    inScene?.traverse(function (obj) {
      if (obj instanceof Mesh) {
        obj.geometry.dispose()
        obj.material.dispose()
      }
    })
    this.glasses = await loadModel(url)

    this.contrast.upMaterial(this.glasses)
    const bbox = new Box3().setFromObject(this.glasses)

    const size = bbox.getSize(new Vector3())

    this.scaleFactor = size.x

    this.glasses.name = 'glasses'
    this.glasses.renderOrder = 1
    this.loading = false
    this.addGlasses()
  }
  createCube() {
    const geometry = new BoxGeometry()
    const material = new MeshBasicMaterial({
      colorWrite: false,
      color: 'red',
      // wireframe: true,
    })
    const mesh = new Mesh(geometry, material)
    mesh.name = 'cube'
    mesh.renderOrder = 999
    // mesh.add(new AxesHelper(10))
    this.cube = mesh
  }
  updateDimensions(width: number, height: number) {
    this.width = width
    this.height = height
    this.needUpdate = true
  }
  updateLandmarks(landmark: NormalizedLandmark[]) {
    this.landmark = landmark
    this.needUpdate = true
  }
  removeGlasses() {
    if (this.scene && this.glasses) {
      this.scene.remove(this.glasses)
    }
  }
  addGlasses() {
    if (this.scene && this.glasses) {
      this.scene.add(this.glasses)
    }
  }
  addCube() {
    if (this.scene && this.cube) {
      this.scene?.add(this.cube)
    }
  }
  updateGlasses() {
    const midEyes = scaleLandmark(this.landmark![168], this.width, this.height)
    const leftEyeInnerCorner = scaleLandmark(this.landmark![33], this.width, this.height)
    const rightEyeInnerCorner = scaleLandmark(this.landmark![263], this.width, this.height)
    const noseBottom = scaleLandmark(this.landmark![200], this.width, this.height)
    const leftEyeUpper1 = scaleLandmark(this.landmark![234], this.width, this.height)
    const rightEyeUpper1 = scaleLandmark(this.landmark![454], this.width, this.height)

    if (this.glasses) {
      this.glasses.position.set(midEyes.x, midEyes.y, midEyes.z)
      this.glasses.position.y -= 0.1
      // === 缩放 ===
      const eyeDist = Math.sqrt(
        (leftEyeUpper1.x - rightEyeUpper1.x) ** 2 +
          (leftEyeUpper1.y - rightEyeUpper1.y) ** 2 +
          (leftEyeUpper1.z - rightEyeUpper1.z) ** 2,
      )
      const scale = eyeDist / this.scaleFactor!
      this.glasses.scale.set(scale * this.zoom, scale * this.zoom, scale * this.zoom)
      this.cube?.scale.set(eyeDist * this.zoom, eyeDist * this.zoom, eyeDist * this.zoom)
      // === 方向向量 ===
      const upVector = new Vector3(
        midEyes.x - noseBottom.x,
        midEyes.y - noseBottom.y,
        midEyes.z - noseBottom.z,
      ).normalize()

      const sideVector = new Vector3(
        leftEyeInnerCorner.x - rightEyeInnerCorner.x,
        leftEyeInnerCorner.y - rightEyeInnerCorner.y,
        leftEyeInnerCorner.z - rightEyeInnerCorner.z,
      ).normalize()

      // 前向量 = side × up
      const forwardVector = new Vector3().crossVectors(sideVector, upVector).normalize().negate()
      // 重新正交化 side 向量
      sideVector.crossVectors(upVector, forwardVector).normalize()

      // === 构造旋转矩阵 ===
      const rotationMatrix = new Matrix4().makeBasis(sideVector, upVector, forwardVector)

      // === 转成四元数 ===
      const q = new Quaternion().setFromRotationMatrix(rotationMatrix)

      // === 应用到眼镜 ===
      this.glasses.quaternion.copy(q)

      // 如果你还需要微调（比如向下压 10°）
      const adjustQ = new Quaternion().setFromAxisAngle(
        new Vector3(1, 0, 0),
        MathUtils.degToRad(-5),
      )
      this.glasses.quaternion.multiply(adjustQ)

      const forwrdVector = new Vector3(0, 0, -1).applyQuaternion(this.glasses.quaternion)
      const cubeGeo = this.cube!.geometry as BoxGeometry

      const cubeDepth = cubeGeo.parameters.depth
      const offsetDistance = cubeDepth * this.cube!.scale.z
      this.cube?.position
        .copy(this.glasses.position)
        .add(forwrdVector.clone().multiplyScalar(offsetDistance))
      this.cube?.quaternion.copy(this.glasses.quaternion)
    }
  }
  updateCube() {}
  update() {
    if (this.needUpdate) {
      const inScene = !!this.scene.getObjectByName('glasses')
      const shoudShow = !!this.landmark?.length
      if (inScene) {
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        shoudShow ? this.updateGlasses() : this.removeGlasses()
      } else if (shoudShow && !this.loading) {
        this.addGlasses()
      }
      this.needUpdate = false
    }
  }
  zoomGlasses(zoom: number) {
    this.zoom = zoom
  }
}
